use std::fs;

use crate::Puzzle;

pub struct SonarSweep {
    depths: Vec<u32>,
}

impl SonarSweep {
    #[allow(dead_code)]
    fn new_with_depths(depths: Vec<u32>) -> Self {
        Self { depths }
    }
}

impl Puzzle for SonarSweep {
    type Answer = u32;

    fn new() -> Self {
        let input = fs::read_to_string("input/day01.md").unwrap();

        let depths = input
            .lines()
            .filter_map(|line| line.trim().parse::<u32>().ok())
            .collect();

        Self { depths }
    }

    fn solve_part1(&self) -> u32 {
        self.depths
            .windows(2)
            .filter(|window| {
                if let [first, second] = window {
                    second > first
                } else {
                    false
                }
            })
            .count()
            .try_into()
            .unwrap_or_default()
    }

    fn solve_part2(&self) -> u32 {
        self.depths
            .windows(3)
            .map(|window| {
                if let [first, second, third] = window {
                    first + second + third
                } else {
                    0
                }
            })
            .collect::<Vec<_>>()
            .windows(2)
            .filter(|window| {
                if let [first, second] = window {
                    second > first
                } else {
                    false
                }
            })
            .count()
            .try_into()
            .unwrap_or_default()
    }
}

#[cfg(test)]
mod test {
    use super::SonarSweep;
    use crate::Puzzle;

    fn new() -> SonarSweep {
        let depths = vec![199, 200, 208, 210, 200, 207, 240, 269, 260, 263];
        SonarSweep::new_with_depths(depths)
    }

    #[test]
    fn part1() {
        let sonar_sweep = new();
        assert_eq!(sonar_sweep.solve_part1(), 7);
    }

    #[test]
    fn part2() {
        let sonar_sweep = new();
        assert_eq!(sonar_sweep.solve_part2(), 5);
    }
}
